#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <dlfcn.h>
#include "inkview.h"

#define CONFIG_FILE STATEPATH "/straycat.cfg"
#define UPDATE 1
#define NOT_UPDATE 0
#define HUMAN -1
#define COMPUTER 1
#define STEP_RIGHT 4
#define STEP_LEFT 2
#define STEP_UP 3
#define STEP_DOWN 1
#define BMP_HEIGHT 64
#define BMP_WIDTH 64
#define BOARD_HEIGHT 320 //5*BMP_HEIGHT
#define BOARD_WIDTH  320 //5*BMP_WIDTH
#define KEEP_MOUSE 1
#define OUTSIDE -1
#define HEADPANEL 80
#define FEETPANEL 105
#define MATRIX "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
                
extern int rand();
extern const ibitmap mouse, cat_right, cat_left, kitten, road1, road2, road3, blank, human_icon, compyter_icon;
ifont *arial16;
int winner = 0, lastwinner = HUMAN, firststart = 1, marker = 2;
int BoardX, BoardY;
int hum = 0, comp = 0, direction = 0;
int Figura = 0, xBoard = 0, yBoard = 0, xStep = 0, yStep = 0;

const int ToDo[5][5] = {
			{0,  1,  1,  2,  4},
			{0,  3,  3,  2,  4},
			{5,  5,  6,  9,  9},
			{7,  7,  6,  11, 11},
			{13, 13, 15, 15, 20}
			};

int	TBoard[7][7];

iconfig * cfg = NULL;
static iconfigedit confedit[] = {
	{ CFG_INDEX, NULL, "Computer position", "Computer position", "computerpos", "0", NULL, NULL},
	{ CFG_INDEX, NULL, "Human position", "Human position", "humanpos", "0", NULL, NULL},
	{ CFG_TEXT, NULL, "Map", "Map MATRIX", "map", MATRIX, NULL, NULL},
	{ CFG_INDEX, NULL, "CatX position", "Cat horizontal position", "catx", "0", NULL, NULL},
	{ CFG_INDEX, NULL, "CatY position", "Cat vertical position", "caty", "0", NULL, NULL},
	{ CFG_INDEX, NULL, "Direction", "Cat move direction", "direction", "0", NULL, NULL},
	{ 0, NULL, NULL, NULL, NULL, NULL, NULL}
};

static imenu mainMenu[] = {
	{ ITEM_HEADER,   0, "Меню", NULL },
	{ ITEM_ACTIVE, 101, "Начать все заново", NULL },
	{ ITEM_ACTIVE, 102, "Помощь", NULL },
	{ ITEM_ACTIVE, 103, "О программе", NULL },
	{ ITEM_ACTIVE, 104, "Выход", NULL },
	{ 0, 0, NULL, NULL }
};

int randint(int u) { //случайное целое число больше нуля, но меньше "u" 
	int  r = rand();
	if (r < 0) r = -r;
	return r%u;
}

void DrawStatusBar() {
	FillArea(0, ScreenHeight() - 25, ScreenWidth(), 25, LGRAY);
	SetFont(arial16, BLACK);
	DrawString(25, ScreenHeight() - 22, " = Навигация |     = Выбор | удерж.      или длинный тап = Меню");		
	DrawSymbol(15, ScreenHeight() - 22, ARROW_UPDOWN);
	DrawSymbol(130,ScreenHeight() - 22, SYMBOL_OK);
	DrawSymbol(280,ScreenHeight() - 22, SYMBOL_OK);
}

void DrawCat(int i, int j) {
	i = BoardX + (i - 1) * BMP_WIDTH;
	j = BoardY + (j - 1) * BMP_HEIGHT;
	if (direction == STEP_LEFT) {
		DrawBitmap(i, j, &cat_left);
	} else {
		DrawBitmap(i, j, &cat_right);
	}
	PartialUpdate(i, j, BMP_HEIGHT, BMP_WIDTH);
}

void Road(int x, int y, int type) {
	switch (type){
	case 0:
		DrawBitmap(x, y, &blank);
		break;
	case 1:
		DrawBitmap(x, y, &road1);
		break;
	case 2:
		DrawBitmap(x, y, &road2);
		break;
	case 3:
		DrawBitmap(x, y, &road3);
		break;
	}
}

void DrawRoad(int i, int j, int type, int updt) {
	i = BoardX + (i - 1) * BMP_WIDTH;
	j = BoardY + (j - 1) * BMP_HEIGHT;
	Road(i, j, type);
	if (updt) {
		PartialUpdateBW(i, j, BMP_HEIGHT, BMP_WIDTH);
	}
}

void BlinkRoad(int i, int j, int type) {
	int k;
	for (k = 0 ; k < 3; k++){
		if (k) DrawRoad(i, j, 0, UPDATE);
		DrawRoad(i, j, type, UPDATE);
	}
}

void ShowMarker(int newpos, int oldpos, int updt) {
	int i, x, y, k;
	newpos--;
	oldpos--;
 	x = BoardX * 2 + BOARD_WIDTH;
	k = (BOARD_HEIGHT - 3 * BMP_HEIGHT) / 4;
	y = BoardY + k + (oldpos) * BMP_HEIGHT + (oldpos) * k;
	DrawRect(x - 3, y - 3, BMP_HEIGHT + 6, BMP_WIDTH + 6, WHITE);
	DrawRect(x - 2, y - 2, BMP_HEIGHT + 4, BMP_WIDTH + 4, WHITE);
	y = BoardY + k + (newpos) * BMP_HEIGHT + (newpos) * k;
	DrawRect(x - 3, y - 3, BMP_HEIGHT + 6, BMP_WIDTH + 6, BLACK);
	DrawRect(x - 2, y - 2, BMP_HEIGHT + 4, BMP_WIDTH + 4, BLACK);
	y = BoardY + k;
	if (updt == UPDATE){
		PartialUpdateBW(x - 3, y - 3, BMP_WIDTH + 6, y + 3 * BMP_HEIGHT + 2 * k + 6);
	} else {
		for (i = 0; i < 3; i++){
			Road(x, y + i * BMP_HEIGHT + i * k, i + 1);
		}
	}
}

void ClearBord() {
	int k, l;
	for (k = 0; k < 7; k++) { //очистка массива игровой площадки
		for (l = 0; l < 7; l++) {
			TBoard[l][k] = 0;
		}
	}
	TBoard[1][1] = 1; // Первый ход: всегда в левый верхний угол ставим перекресток	
}

void StartNewGame() {
	int a, b;
	a = 1 + randint(2); // Выбираем случайное направление движения 1 или 2
	b = 1 + (a == 1);
	if (lastwinner == HUMAN) {
		if (a == 1) { direction = STEP_DOWN;} else {direction = STEP_RIGHT;}
		xBoard = xStep = a;
		yBoard = yStep = b;
	} else {
		if (a == 1) { direction = STEP_RIGHT;} else { direction = STEP_DOWN; }
		BlinkRoad(a, b, 2);
		TBoard[a][b] = 2;
		xBoard = xStep = 2;
		yBoard = yStep = 2;
	}
	firststart = 0;
}

void DrawBoardFullUpdate() {
	int i, j, k;
	ClearScreen();
	DrawStatusBar();
	BoardX = (ScreenWidth() - BOARD_WIDTH - BMP_WIDTH) / 3;
	BoardY = HEADPANEL + (ScreenHeight() - BOARD_HEIGHT - FEETPANEL) / 2;
	SetFont(arial16, BLACK);
	FillArea(0, 0, ScreenWidth(), 80, LGRAY);
	DrawBitmap(25, 0, &compyter_icon);
	DrawBitmap(25, 40, &human_icon);
	DrawBitmap(ScreenWidth() - 105, 10, &kitten);
	if (ScreenWidth() == 600) {
		k = (ScreenWidth() - 224) / 10;
	} else {
		k = (ScreenWidth() - 200) / 10;
	}
	DrawBitmap(90 + comp * k, 10, &mouse);
	DrawBitmap(90 + hum * k, 45, &mouse);
	DrawRect(BoardX - 1, BoardY - 1, BOARD_HEIGHT + 2, BOARD_WIDTH + 2, BLACK);
	for (i = 1; i < 6; i++) {
        for (j = 1; j < 6; j++) {
 			DrawRoad(i, j, TBoard[i][j], NOT_UPDATE);
		}
	}	
	DrawBitmap(BoardX + 4 * BMP_WIDTH, BoardY + 4 * BMP_HEIGHT + 34, &mouse);
	ShowMarker(marker, marker, NOT_UPDATE);
	FullUpdate();
	if (firststart) StartNewGame();
	DrawCat(xStep, yStep);
}

void Tracer() { // Tells: "Where to road heads to?" 
	int tmp = 0;
	xStep = xBoard;
	yStep = yBoard;
	while ( !tmp ){
		if (direction == STEP_DOWN){//если направление вниз
			if (Figura == 1) {tmp = STEP_DOWN; yStep++;}// шаг вниз
			if (Figura == 2) {tmp = STEP_RIGHT; xStep++;}//шаг вправо
			if (Figura == 3) {tmp = STEP_LEFT; xStep--;}//шаг влево
		}
		if (direction == STEP_LEFT){//если направление влево
			if (Figura == 1) {tmp = STEP_LEFT; xStep--;}//шаг влево
			if (Figura == 2) {tmp = STEP_UP; yStep--;}//шаг вверх
			if (Figura == 3) {tmp = STEP_DOWN; yStep++;}//шаг вниз
		}
		if (direction == STEP_UP){//если направление вверх
			if (Figura == 1) {tmp = STEP_UP; yStep--;}//шаг вверх
			if (Figura == 2) {tmp = STEP_LEFT; xStep--;}//шаг влево
			if (Figura == 3) {tmp = STEP_RIGHT; xStep++;}//шаг вправо
		}
		if (direction == STEP_RIGHT){//если направление вправо
			if (Figura == 1) {tmp = STEP_RIGHT; xStep++;}//шаг вправо
			if (Figura == 2) {tmp = STEP_DOWN; yStep++;}//шаг вниз
			if (Figura == 3) {tmp = STEP_UP; yStep--;}//шаг вверх
		}
		direction = tmp;
		if (TBoard[xStep][yStep] != 0) {
			Figura = TBoard[xStep][yStep]; 
			tmp = 0;
		}
	}
}
	
int Check() { // выиграл кто нибудь?
	if (xStep == 5 && yStep == 5) return KEEP_MOUSE; // Поймал мышь
	if (xStep == 0 || xStep == 6 || yStep == 0 || yStep == 6) return OUTSIDE; //Вышел за пределы игрового поля
	return 0;
}

void Win(int w) { // Объявляем победителя
	if (w == COMPUTER){
		comp++;
		lastwinner = COMPUTER;
		if (comp == 10){
			hum = comp = 0;
			Message(0, "Победил компьютер", "Ты не настоящий дворовой кот, ты просто разнеженное домашнее котейко.",60000);
		} else {
			Message(0, "Победил компьютер", "Эту партию выиграл компьютер.",10000);
		}
	} else {
		hum++;
		lastwinner = HUMAN;
		if (hum == 10){
			hum=comp=0;
			Message(0, "Победил Игрок", "О да, ты насстоящий дворовой кот.", 60000);
		} else {
			Message(0, "Победил Игрок", "Эту партию выиграл игрок",10000);	
		}
	}
	firststart = 1;
	ClearBord();
	DrawBoardFullUpdate();	
}

void Complay() { // ход компьютера
	int pl = 0, dd = direction, v = 0;
	int v1, v2, v3, v4;
	while (v < 4){
		v++;
		Figura = v;
		direction = dd;
		Tracer();
		if (Check()  == 1) {
			Figura = v; 
			v = 5;
		}
		v = v + (v == 3);
	}
	if (v == 5) pl = 1;	// Удачный ход
	if (!pl) {
		v = 0;
		while (v < 4){
			v++;
			Figura = v;
			direction = dd;
			Tracer();
			if (Check() == -1 || xStep + yStep == 9) {
				v = v + (v == 3);
				break;
			}
			if (ToDo[yBoard][xBoard] == ToDo[yStep][xStep]) {
				Figura = v;
				v = 5;
			}
			v = v + (v == 3); 
		}
	}
	if (v == 5) pl = 1;
	if (!pl) {  // выбор хода случайным образом
		if (randint(2) == 0) {
			v1 = 3; 
			v2 = 1; 
			v3 = -1;
		} else {
			v1 = 1;
			v2 = 3;
			v3 = 1;
		}
		v4 = 0;
		v = v1;
		while (v4 < 4){	
			v4++;
			Figura = v;
			direction = dd;
			Tracer();
			if (Check() > -1 && xStep + yStep < 9) {
				Figura = v; 
				v = v2 + v3;
				v4 = 4;
			}
			v = v + v3;
			v4 = v4 + (v4 == 3);
		}	  
	}
	BlinkRoad(xBoard, yBoard, Figura);
	TBoard[xBoard][yBoard] = Figura;
	if (Check() == KEEP_MOUSE) {//Выиграл компьютер
		Win(COMPUTER);
		return;
	}
 	if (Check() == OUTSIDE) {//Выиграл игрок
		Win(HUMAN);
		return;
	}
	xBoard = xStep;
	yBoard = yStep;
	DrawCat(xStep, yStep);
}

void HumanPlay() {// ход игрока
	DrawRoad(xBoard, yBoard, marker, UPDATE);
	TBoard[xBoard][yBoard] = Figura = marker;
	Tracer();
	if (Check() == KEEP_MOUSE) {//Выиграл игрок
		Win(HUMAN); 
		return;
	}
	if (Check() == OUTSIDE) {//Выиграл компьютер 
		Win(COMPUTER);
		return;
	}
	yBoard = yStep;
	xBoard = xStep;
	Complay();  //передаем ход компьютеру	
}

int selectedIndex = 0;
void mainMenuHandler(int index) {
	selectedIndex = index;
	switch (index) {
	case 101:
		hum = comp = 0;
		firststart = 1;
		ClearBord();
		DrawBoardFullUpdate();
		break;
	case 102:
		Message(0, "Помощь", "  Почувствуй себя настоящим блудным котом!\n  Пройди по лабиринту улиц в поисках добычи и приключений!\n  Добудь десять мышей для своей кошечки!\n  И помни, злые соседские коты будут стараться сбить тебя с пути и выкинуть за пределы квартала, или увести из под носа твою добычу, но если ты настоящий дворовой забияка, а не какой нибудь там домашний «Мурзик», ты с честью преодолеешь все препятствия.\n\n"
"  Игра начинается в левом верхнем углу игрового поля размером 5х5. Первый ход предоставляется последнему выигравшему партнеру. В распоряжении игроков есть три вида перекрестков, которые и определяют каким будет путь. Цель находится в правом нижнем углу, но совсем не обязательно стремится добраться туда первым, можно также вытолкнуть противника за пределы игрового поля.\n\n"
"  Держи хвост пистолетом!\n"
"  Удачи!",60000);
		break;
	case 103:
		Message(0, "О программе", "Straycat для PocketBook.\nОригинальный алгоритм: Антон Хлебаров (1987 г.).\nАдаптация для PocketBook: Antuan (2009 г.).", 10000);
		break;
	case 104:
		CloseApp();
		break;
	}
}

void ShowMenu() {
	OpenMenu(mainMenu, selectedIndex, 20, 20, mainMenuHandler);
}

void Load_Config() {
	char *q;
	int i, j;
	if(!cfg) cfg = OpenConfig(CONFIG_FILE, confedit);
	xBoard = xStep = ReadInt(cfg, "catx", 0);
	yBoard = yStep = ReadInt(cfg, "caty", 0);
	direction = ReadInt(cfg, "direction", 0);
	hum = ReadInt(cfg, "humanpos", 0);
	comp = ReadInt(cfg, "computerpos", 0);
	q = ReadString(cfg, "map", MATRIX);
	ClearBord();
	if (xStep == 0 && yStep == 0){	
		firststart = 1;
	} else {
		firststart = 0;
		for (i = 1; i < 6; i++) { 
			for (j = 1; j < 6; j++) {
				TBoard[j][i] = atoi(q);
				while (*q != 0 && *q != ',') q++;
				q++;
			}
		}
	}
}

void Save_Config() {
	char buf[500] = "";
	char str[5];
	int i, j;
	if(!cfg) cfg = OpenConfig(CONFIG_FILE, confedit);
	WriteInt(cfg, "humanpos", hum);
	WriteInt(cfg, "computerpos", comp);
	WriteInt(cfg, "catx", xStep);
	WriteInt(cfg, "caty", yStep);
	WriteInt(cfg, "direction", direction);
	for (i = 1; i < 6; i++) { 
		for (j = 1; j < 6; j++) {
			sprintf(str, "%i,", TBoard[j][i]);
			strcat(buf, str);
		}
	}
	WriteString(cfg, "map", buf);
	SaveConfig(cfg);
}

void TimerHumanPlay(){
	HumanPlay();
	ClearTimer(TimerHumanPlay);
}

void KeyPressed(int key) {
	int temp = marker;
	switch (key) {
	case KEY_BACK:
		CloseApp();
		return;
	case KEY_UP:
		if (marker > 1) {marker--;} else {marker = 3;}
		ShowMarker(marker, temp, UPDATE);
		break;
	case KEY_DOWN:
		if (marker < 3) {marker++;} else {marker = 1;}
		ShowMarker(marker, temp, UPDATE);
		break;
	case KEY_OK:
		SetWeakTimer("MYTIMER", TimerHumanPlay, 0);
		break;
	case KEY_MENU:
		ShowMenu();
		break;
	}
}

void PointerPressed(int x, int y){
	int upd = 0;
	int temp = marker;
	int k = (BOARD_HEIGHT - 3*BMP_HEIGHT)/4 ;
	int i = BoardX * 2 + BOARD_WIDTH;
	int k1 = BoardY + k;
	if (x > i && x < (i + BMP_WIDTH)){
		if (y > k1 && y < (k1 + BMP_HEIGHT)) {
			marker = 1;
			upd = 1;
		}
		k1 = k1 + k + BMP_HEIGHT;
		if (y > k1 && y < (k1 + BMP_HEIGHT)) {
			marker = 2;
			upd = 1;
		}
		k1 = k1 + k + BMP_HEIGHT;
		if (y > k1 && y < (k1 + BMP_HEIGHT)) {
			marker = 3;
			upd = 1;
		}
	}
	if (upd == 1) {
		ShowMarker(marker, temp, UPDATE);
		SetWeakTimer("MYTIMER", TimerHumanPlay, 0);
	}
}
 
int main_handler(int type, int par1, int par2) {
	if (type == EVT_INIT) {
		arial16 = OpenFont("LiberationSans", 16, 1);
		Load_Config();
	} else if (type == EVT_SHOW ) {
		DrawBoardFullUpdate();
	} else if (type == EVT_KEYREPEAT && par1 == KEY_NEXT) {
		PointerPressed(500, 400);
	} else if (type == EVT_POINTERUP){
		PointerPressed(par1, par2);
	} else if (type == EVT_POINTERLONG){
		KeyPressed(KEY_MENU);
	} else if (type == EVT_KEYPRESS && par1 != KEY_OK && par1 != KEY_PREV) {
		KeyPressed(par1);
	} else if (type == EVT_KEYREPEAT && par1 == KEY_OK) {
		KeyPressed(KEY_MENU);
	} else if (type == EVT_KEYREPEAT && par1 == KEY_PREV) {
		KeyPressed(KEY_BACK);
	} else if (type == EVT_KEYRELEASE && par1 == KEY_OK && par2 == 0) {
		KeyPressed(KEY_OK);
	} else if (type == EVT_KEYRELEASE && par1 == KEY_PREV && par2 == 0) {
		KeyPressed(KEY_PREV);
	} else if (type == EVT_ORIENTATION){
		SetOrientation(par1);
		DrawBoardFullUpdate() ;
	} else if (type == EVT_EXIT) {
		Save_Config();
	}
	return 0;
}

int main(int argc, char **argv) {
	InkViewMain(main_handler);	
	return 0;
}
